#include "PreCompile.hpp"
#include "Settings.hpp"
#include "LibBuildNGInit.hpp"

#ifdef WINDOWS
#include <windows.h>
#endif

namespace BuildNG
{
  SettingsFile *Settings::global_settings_file = 0;
  
  void Settings::init(const Glib::ustring &settings_file)
  {
    assert(!global_settings_file);

    global_settings_file = new SettingsFile(settings_file);
  }

  void Settings::shutdown()
  {
    delete global_settings_file;
    global_settings_file = 0;
  }

  Glib::ustring Settings::get_setting_string(const Glib::ustring &xpath)
  {
    return global_settings_file->get_setting_string(xpath);
  }

  bool Settings::try_get_setting_string(const Glib::ustring &xpath,Glib::ustring &res)
  {
    return global_settings_file->try_get_setting_string(xpath,res);
  }

  Glib::ustring Settings::get_setting_path(const Glib::ustring &xpath)
  {
    return global_settings_file->get_setting_path(xpath);
  }

  std::vector<Glib::ustring> Settings::get_setting_strarr(const Glib::ustring &xpath)
  {
    return global_settings_file->get_setting_strarr(xpath);
  }

  ////////
  // SettingsFile
  SettingsFile::SettingsFile(const Glib::ustring &file_name)
  {
    doc = new Xml::Document(file_name);

    settings_file_path = Glib::Path::get_absolute_path(file_name);
  }

  SettingsFile::~SettingsFile()
  {
    delete doc;
  }

  Glib::ustring SettingsFile::get_setting_string(const Glib::ustring &xpath)
  {
    assert_libbuildng_initialized();
    assert(doc);

    Glib::ustring ret;
    if(!try_get_setting_string(xpath,ret))
      throw Exception("Setting \"" + xpath + "\" not found.");
    return ret;
  }

  bool SettingsFile::try_get_setting_string(const Glib::ustring &xpath,Glib::ustring &res)
  {
    assert_libbuildng_initialized();
    assert(doc);

    Xml::NodeSet nodes = doc->get_root_node()->find(xpath);
    if(nodes.size() == 0)
      return false;
    else if(nodes.size() != 1)
      throw Exception("Setting \"" + xpath + "\" defined multiple times");

    res = node_to_text(nodes[0]);
    return true;    
  }

  Glib::ustring SettingsFile::get_setting_path(const Glib::ustring &xpath)
  {
    Glib::ustring setting_value = get_setting_string(xpath);
    if(Glib::path_is_absolute(setting_value))
      return setting_value;
    else
    {
      Glib::ustring abs_path = Glib::build_filename(
        Glib::path_get_dirname(settings_file_path),setting_value);
      return Glib::Path::canonicalize_path(abs_path);
    }
  }

  bool SettingsFile::try_get_setting_strarr(const Glib::ustring &xpath,std::vector<Glib::ustring> &res)
  {
    assert_libbuildng_initialized();
    assert(doc);

    Xml::NodeSet nodes = doc->get_root_node()->find(xpath);
    if(nodes.size() == 0)
      return false;
    
    res.clear();
    for(int i = 0;i < (int)nodes.size();i++)
    {
      res.push_back(node_to_text(nodes[i]));
    }

    return true;
  }
  
  std::vector<Glib::ustring> SettingsFile::get_setting_strarr(const Glib::ustring &xpath)
  {
    std::vector<Glib::ustring> ret;
    if(!try_get_setting_strarr(xpath,ret))
      throw Exception("Setting \"" + xpath + "\"not found.");
    return ret;
  }

  Glib::ustring SettingsFile::node_to_text(const Xml::Node *node)
  {
    switch(node->get_node_type())
    {
    case Xml::XML_ELEMENT_NODE:
      {
        assert(dynamic_cast<const Xml::Element*>(node));
        const Xml::Element *as_element = static_cast<const Xml::Element*>(node);
        return element_content_to_text(*as_element);
      }

    case Xml::XML_ATTRIBUTE_NODE:
      {
        assert(dynamic_cast<const Xml::Attribute*>(node));
        const Xml::Attribute *as_attribute = static_cast<const Xml::Attribute*>(node);
        return as_attribute->get_value();
      }
    }

    assert(!"Not implemented yet");
    return "";
  }

  Glib::ustring SettingsFile::element_content_to_text(const Xml::Element &element)
  {
    Glib::ustring ret;

    Xml::NodeList children = element.get_children();
    for(int i = 0;i < (int)children.size();i++)
    {
      switch(children[i]->get_node_type())
      {
      case Xml::XML_ELEMENT_NODE:
        {
          Xml::Element *element = dynamic_cast<Xml::Element*>(children[i]);
          assert(element);

#ifdef WINDOWS
          if(element->get_name() == "win-registry")
          {
            Glib::ustring key_name = element->get_attribute_value("key");
            Glib::ustring value_name = element->get_attribute_value("value");

            HKEY key;
            try
            {
              size_t first_slash = key_name.find_first_of("\\");
              Glib::ustring base_key_name = key_name.substr(0,first_slash);
              Glib::ustring sub_key = key_name.substr(first_slash + 1);

              HKEY base_key;
              if(base_key_name == "HKEY_CLASSES_ROOT")
                base_key = HKEY_CLASSES_ROOT;
              else if(base_key_name == "HKEY_CURRENT_USER")
                base_key = HKEY_CURRENT_USER;
              else if(base_key_name == "HKEY_LOCAL_MACHINE")
                base_key = HKEY_LOCAL_MACHINE;
              else if(base_key_name == "HKEY_USERS")
                base_key = HKEY_USERS;
              else
              {
                assert(!"Not implemented yet");
              }

              if(RegOpenKeyEx(base_key,to_wstring(sub_key).c_str(),0,KEY_QUERY_VALUE,&key) != ERROR_SUCCESS)
              {
                assert(!"not implemented yet");
              }

              wchar_t gtk_path_wide[MAX_PATH];
              DWORD gtk_path_size = MAX_PATH * sizeof(wchar_t);
              if(RegQueryValueEx(key,to_wstring(value_name).c_str(),NULL,NULL,(LPBYTE)&gtk_path_wide,&gtk_path_size) != ERROR_SUCCESS)
              {
                assert(!"not implemented yet");
              }

              ret += to_ustring(gtk_path_wide);

              RegCloseKey(key);
            }
            catch(...)
            {
              RegCloseKey(key);
              throw;
            }
          }
#endif
        }
        break;

      case Xml::XML_TEXT_NODE:
      case Xml::XML_CDATA_SECTION_NODE:
        {
          Xml::ContentNode *as_content_node = dynamic_cast<Xml::ContentNode*>(children[i]);
          assert(as_content_node);

          ret += as_content_node->get_content();
        }
        break;
      }
    }

    trim(ret);
    return ret;
  }
}
